/*******************************************************************************
 * Copyright (c) 2003, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;

import org.eclipse.draw2d.geometry.Dimension;

/**
 * @author Pratik Shah
 * @since 3.0
 */
public class ImageUtilities {

/**
 * Returns a new Image with the given String rotated left (by 90 degrees).  The String
 * will be rendered using the provided colors and fonts.  The client is responsible for
 * disposing the returned Image.  Strings cannot contain newline or tab characters.  This
 * method MUST be invoked from the user-interface (Display) thread.
 * 
 * @param string the String to be rendered
 * @param font the font
 * @param foreground the text's color
 * @param background the background color
 * @return an Image which must be disposed
 */
public static Image createRotatedImageOfString(String string, Font font, 
                                               Color foreground, Color background) {
	Display display = Display.getCurrent();
	if (display == null)
		SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS);
	
	FontMetrics metrics = FigureUtilities.getFontMetrics(font);
	Dimension strSize = FigureUtilities.getStringExtents(string, font);
	Image srcImage = new Image(display, strSize.width, metrics.getAscent());
	GC gc = new GC(srcImage);
	gc.setFont(font);
	gc.setForeground(foreground);
	gc.setBackground(background);
	gc.fillRectangle(srcImage.getBounds());
	gc.drawString(string, 0, 0 - metrics.getLeading());
	Image result = createRotatedImage(srcImage);
	gc.dispose();
	srcImage.dispose();
	return result;
}

/**
 * Returns a new Image that is the given Image rotated left by 90 degrees.  The client is
 * responsible for disposing the returned Image.  This method MUST be invoked from the
 * user-interface (Display) thread.
 * 
 * @param	srcImage	the Image that is to be rotated left
 * @return	the rotated Image (the client is responsible for disposing it)
 */
public static Image createRotatedImage(Image srcImage) {
	Display display = Display.getCurrent();
	if (display == null)
		SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS);

	ImageData srcData = srcImage.getImageData();
	ImageData destData;
	if (srcData.depth < 8)
		destData = rotatePixelByPixel(srcData);
	else
		destData = rotateOptimized(srcData);
	
	return new Image(display, destData);
}

/**
 * Creates an ImageData representing the given <code>Image</code> shaded with the given
 * <code>Color</code>.
 * 
 * @param fromImage	Image that has to be shaded
 * @param shade		The Color to be used for shading
 * @return A new ImageData that can be used to create an Image.
 */	
public static ImageData createShadedImage(Image fromImage, Color shade) {
	org.eclipse.swt.graphics.Rectangle r = fromImage.getBounds();
	ImageData data = fromImage.getImageData();
	PaletteData palette = data.palette;
	if (!palette.isDirect) {
		/* Convert the palette entries */
		RGB [] rgbs = palette.getRGBs();
		for (int i = 0; i < rgbs.length; i++) {
			if (data.transparentPixel != i) {
				RGB color = rgbs [i];
				color.red = determineShading(color.red, shade.getRed());
				color.blue = determineShading(color.blue, shade.getBlue());
				color.green = determineShading(color.green, shade.getGreen());
			}
		}
		data.palette = new PaletteData(rgbs);
	} else {
		/* Convert the pixels. */
		int[] scanline = new int[r.width];
		int redMask = palette.redMask;
		int greenMask = palette.greenMask;
		int blueMask = palette.blueMask;
		int redShift = palette.redShift;
		int greenShift = palette.greenShift;
		int blueShift = palette.blueShift;
		for (int y = 0; y < r.height; y++) {
			data.getPixels(0, y, r.width, scanline, 0);
			for (int x = 0; x < r.width; x++) {
				int pixel = scanline[x];
				int red = pixel & redMask;
				red = (redShift < 0) ? red >>> -redShift : red << redShift;
				int green = pixel & greenMask;
				green = (greenShift < 0) ? green >>> -greenShift : green << greenShift;
				int blue = pixel & blueMask;
				blue = (blueShift < 0) ? blue >>> -blueShift : blue << blueShift;
				red = determineShading(red, shade.getRed());
				blue = determineShading(blue, shade.getBlue());
				green = determineShading(green, shade.getGreen());
				red = (redShift < 0) ? red << -redShift : red >> redShift;
				red &= redMask;
				green = (greenShift < 0) ? green << -greenShift : green >> greenShift;
				green &= greenMask;
				blue = (blueShift < 0) ? blue << -blueShift : blue >> blueShift;
				blue &= blueMask;
				scanline[x] = red | blue | green;
			}
			data.setPixels(0, y, r.width, scanline, 0);
		}
	}
	return data;
}

private static int determineShading(int origColor, int shadeColor) {
	return (origColor + shadeColor) / 2;
}

private static ImageData rotateOptimized(ImageData srcData) {
	int bytesPerPixel = Math.max(1, srcData.depth / 8);
	int destBytesPerLine = ((srcData.height * bytesPerPixel - 1) / srcData.scanlinePad + 1) 
			* srcData.scanlinePad;
	byte[] newData = new byte[destBytesPerLine * srcData.width];
	for (int srcY = 0; srcY < srcData.height; srcY++) {
		for (int srcX = 0; srcX < srcData.width; srcX++) {
			int destX = srcY;
			int destY = srcData.width - srcX - 1;
			int destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel);
			int srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel);
			System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel);
		}
	}
	return new ImageData(srcData.height, srcData.width, srcData.depth, srcData.palette, 
			srcData.scanlinePad, newData);
}

private static ImageData rotatePixelByPixel(ImageData srcData) {
	ImageData destData = new ImageData(srcData.height, srcData.width, srcData.depth, 
			srcData.palette); 
	for (int y = 0; y < srcData.height; y++) {
		for (int x = 0; x < srcData.width; x++) {
			destData.setPixel(y, srcData.width - x - 1,	srcData.getPixel(x, y));
		}
	}
	return destData;
}

}
